package inet.app.model.util;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;

public class BitmapLargeManager {

	private static BitmapLargeManager instance;
	private Context mContext;
	private LruCache<Integer, Bitmap> mMemoryCache; // key is resource id
	private AsyncTask<Integer, Void, Boolean> mLoadBitmapTask;

	public static BitmapLargeManager getInstance(Context context) {
		if (instance == null) {
			instance = new BitmapLargeManager(context);
		}
		return instance;
	}

	public BitmapLargeManager(Context context) {
		this.mContext = context;
		initMemoryCache();
	}

	private void initMemoryCache() {
		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
		SmartLog.d("maxMemory for lruCache = " + maxMemory);

		// Use 1/8th of the available memory for this memory cache.
		final int cacheSize = maxMemory / 8;

		mMemoryCache = new LruCache<Integer, Bitmap>(cacheSize) {
			@Override
			protected int sizeOf(Integer key, Bitmap bitmap) {
				// The cache size will be measured in kilobytes rather than
				// number of items.
				return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
			}
		};
	}

	/**
	 * Load bitmap from cache
	 * 
	 * @param resId
	 *            resource id of bitmap
	 * @return
	 */
	public Bitmap getBitmapFromCache(int resId) {
		return mMemoryCache.get(resId);
	}

	/**
	 * Remove bitmap from cache
	 * 
	 * @param resId
	 *            rersource id of bitmap
	 */
	public void removeBitmap(int resId) {
		if (mMemoryCache.get(resId) != null) {
			mMemoryCache.remove(resId);
			System.gc();
		}
	}

	/**
	 * Load large bitmap
	 * 
	 * @param resId
	 *            resource id of bitmap
	 * @param listener
	 *            called when load bitmap finish
	 */
	public void loadLargeBimap(final int resId, final int width,
			final int height, final LoadBitmapListener listener) {

		if (mLoadBitmapTask != null) {
			SmartLog.e("Loading other bitmap...");
			return;
		}
		mLoadBitmapTask = new AsyncTask<Integer, Void, Boolean>() {
			@Override
			protected Boolean doInBackground(Integer... params) {
				try {
					Bitmap largeBitmap;
					int size = params.length;
					for (int i = 0; i < size; i++) {
						if (mMemoryCache.get(params[i]) != null) {
							// exist bitmap in cache
							continue;
						}
						largeBitmap = scaleBitmap(mContext.getResources(),
								params[i], width, height);
						mMemoryCache.put(params[i], largeBitmap);
					}
				} catch (OutOfMemoryError e) {
					SmartLog.e(e.toString());
					return false;
				} catch (Exception e) {
					SmartLog.e(e.toString());
					return false;
				}
				return true;
			}

			@Override
			protected void onPostExecute(Boolean result) {
				super.onPostExecute(result);
				mLoadBitmapTask = null;
				if (listener != null) {
					listener.onLoadFinish(resId, result);
				}
			}

		};
		mLoadBitmapTask.execute(resId);
	}

	public static Bitmap scaleBitmap(Resources res, int resId, int reqWidth,
			int reqHeight) {
		// create bitmap options
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeResource(res, resId, options);

		// Calculate inSampleSize
		options.inSampleSize = calculateInSampleSize(options, reqWidth,
				reqHeight);

		// Decode bitmap with inSampleSize set
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeResource(res, resId, options);
	}

	/**
	 * Change quality of bitmap follow size
	 * 
	 * @param options
	 * @param reqWidth
	 * @param reqHeight
	 * @return
	 */
	private static int calculateInSampleSize(BitmapFactory.Options options,
			int reqWidth, int reqHeight) {
		// Raw height and width of image
		final int height = options.outHeight;
		final int width = options.outWidth;
		int inSampleSize = 1;

		if (height > reqHeight || width > reqWidth) {
			// Calculate ratios of height and width to requested height and
			// width
			final int heightRatio = Math.round((float) height
					/ (float) reqHeight);
			final int widthRatio = Math.round((float) width / (float) reqWidth);
			// Choose the smallest ratio as inSampleSize value, this will
			// guarantee
			// a final image with both dimensions larger than or equal to the
			// requested height and width.
			inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
		}
		return inSampleSize;
	}

	/**
	 * Called when finish load large bitmap
	 */
	public interface LoadBitmapListener {
		void onLoadFinish(int resId, boolean isSuccess);
	}

}
